#include "Preprocessing.h"           //头文件

/*自适应阈值化函数*/
//函数将形参的自身地址赋予该形参。
AdaptiveThreshold::AdaptiveThreshold(struct PreprocessParam &preprocess_param)
{
    this->preprocess_param = &preprocess_param;
}

Preprocessing::Preprocessing()
{
    //设定element_size大小为5
    element_size = 5;
}

Preprocessing::~Preprocessing()
{
    //getStructuringElement()函数会返回指定形状和尺寸
    element1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1, 5));
    element2 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 7));
}
/*自定义的自适应阈值函数*/
//src为输入图像，dst为输出图像，method为阈值处理方法。morphology形态学，iteration迭代，filter滤波。
void AdaptiveThreshold::adaptiveThresholdProc(cv::Mat src, cv::Mat &dst, int method, int morphology, int iteration, int filter)
{
    switch (method)//阈值处理方法
    {
    case 0:  // red
    {
        //建立rgbChannels为RGB的三通道容器
        std::vector<cv::Mat> rgbChannels(3);
        //分离颜色通道，将输入图像三通道分离出来
        cv::split(src, rgbChannels);
        //图像gray中的像素值全部为对应的红蓝像素之差
        cv::Mat gray = rgbChannels[2] - rgbChannels[0];
        /*固定阈值操作*/
        //将gray图像进行阈值判断，输出到dst图像中，阈值具体为gray_thre_min，最大值为255。
        cv::threshold(gray, dst, preprocess_param->gray_thre_min, 255, cv::THRESH_BINARY);
        //阈值类型 THRESH_BINARY，若大于阈值，则为最大值，否则为0.
        break;
    }
    case 1:  // blue
    {
        //建立rgbChannels为RGB的三通道容器
        std::vector<cv::Mat> rgbChannels(3);
        //分离颜色通道，将输入图像三通道分离出来
        cv::split(src, rgbChannels);
        //图像gray中的像素值全部为对应的蓝红像素之差
        cv::Mat gray = rgbChannels[0] - rgbChannels[2];
        //将gray图像进行阈值判断，输出到dst图像中，阈值具体为gray_thre_min，最大值为255。
        cv::threshold(gray, dst, preprocess_param->gray_thre_min, 255, cv::THRESH_BINARY);
        break;
    }
    case 2:  // red过曝光
    {
        //减小红色占比
        mulAndIterRedTre(src, dst, preprocess_info_rune);
    }
    case 3:  // blue过曝光
    {
        //减小蓝色占比
        mulAndIterBlueTre(src, dst, preprocess_info_rune);
    }
    default:
        break;
    }
}

void AdaptiveThreshold::mulAndIterRedTre(cv::Mat src, cv::Mat &bin, PreprocessInfo &preprocess_info)
{
    //设定两个图像大小与输入图像一致，8位单通道图像。
    cv::Mat gray_g = cv::Mat::zeros(src.size(), CV_8UC1);
    cv::Mat gray_r = cv::Mat::zeros(src.size(), CV_8UC1);

    // 因为常量会比变量快，所以当图像通道确定时，所以通道用常量
    int srcRows = src.cols * 3;
    //循环遍历输入图像的所有行
    for(int i = 0; i < src.rows; i++)
    {
        /*().data为图像()的首地址*/
        //p_src为输入图像的每行首地址
        uchar *p_src = src.data+srcRows * i;
        //p_gray_g为图像gray_g每行的首地址
        uchar *p_gray_g = gray_g.data + gray_g.cols * i;
        //p_gray_r为图像gray_r每行的首地址
        uchar *p_gray_r = gray_r.data + gray_r.cols * i;
        int jj = 0;
        //循环遍历输入图像的每一个像素（包含3个通道）
        for(int j = 0; j < srcRows; j += 3)
        {
            //gray_g图像的像素值为输入图像第二个通道的像素值（即绿色通道像素值）
            *(p_gray_g + jj) = *(p_src + j + 1);
            //p_gray_r图像的像素值为输入图像三通道的加权和（蓝*0.1+绿*0.2+红*0.7）
            *(p_gray_r + jj) = *(p_src + j) * 0.1 + *(p_src + j + 1) * 0.2 + *(p_src + j + 2) * 0.7;
            jj++;
        }
    }
    // 最大灰度
    //将图像gray_r的首位像素值设为最大灰度值
    uchar gray_max = gray_r.data[0];
    for(int i = 0; i < gray_r.rows; i++)
    {
        //p为图像gray_r每行的首地址
        uchar *p = gray_r.data + i * gray_r.cols;
        //循环遍历gray_r的每个像素（gray_r为单通道图像）
        for(int j = 0; j < gray_r.cols; j++)
        {
            //如果图像gray_r中某个像素值大于最大灰度值的设定值，则该像素值为最大灰度值
            if(*(p + j) > gray_max)
                gray_max = *(p + j);
        }
    }

    int &thre = preprocess_info.thre;
    //设定阈值大小，为gray_r图像的灰度最大值*gray_max_w/10+gray_r图像的像素平均值*gray_avg_w / 1.
    thre = gray_max * preprocess_param->gray_max_w / 10. + avgGrayThreshold(gray_r) * preprocess_param->gray_avg_w / 1.;
    //设定阈值范围，在（gray_thre_min，gray_thre_max）之间
    VAL_LIMIT(thre, preprocess_param->gray_thre_min, preprocess_param->gray_thre_max);
    //如果gray_r图像中的阈值大于thre，则为255，小于thre，则为0，并将最后结果保存再bin中
    cv::threshold(gray_r, bin, thre, 255, 0);//0为第一种阈值操作，若大于阈值，则为最大值，否则为0
}

void AdaptiveThreshold::mulAndIterBlueTre(cv::Mat src, cv::Mat &bin, PreprocessInfo &preprocess_info)
{
    //设定两个图像大小与输入图像一致，8位单通道图像。
    cv::Mat gray_b = cv::Mat::zeros(src.size(), CV_8UC1);
    cv::Mat gray_g = cv::Mat::zeros(src.size(), CV_8UC1);

    int srcRows = src.cols * 3;
    //循环遍历输入图像的所有像素
    for(int i = 0; i < src.rows; i++)
    {
        uchar *p_src = src.data + srcRows * i;
        uchar *p_gray_b = gray_b.data + gray_b.cols * i;
        uchar *p_gray_g = gray_g.data + gray_g.cols * i;

        int jj = 0;
        for(int j = 0; j < srcRows; j += 3)
        {
            //p_gray_b图像的像素值为输入图像三通道的加权和（蓝*0.7+绿*0.2+红*0.1）
            *(p_gray_b + jj) = *(p_src + j) * 0.7 + *(p_src + j + 1) * 0.2 + *(p_src + j + 2) * 0.1;
            //gray_g图像的像素值为输入图像第二个通道的像素值（即绿色通道像素值）
            *(p_gray_g + jj) = *(p_src + j + 1);
            jj++;
        }
    }
    //最大灰度
    //求出gray_b图像中的灰度最大值
    uchar gray_max = gray_b.data[0];
    for(int i = 0; i < gray_b.rows; i++)
    {
        uchar *p = gray_b.data + i * gray_b.cols;
        for(int j = 0; j < gray_b.cols; j++)
        {
            if(*(p + j) > gray_max)
                gray_max = *(p + j);
        }
    }
    
    int &thre = preprocess_info.thre;
    //设定阈值
    thre = gray_max * preprocess_param->gray_max_w / 10. + avgGrayThreshold(gray_b) * preprocess_param->gray_avg_w / 1.;
    //设定阈值范围
    VAL_LIMIT(thre, preprocess_param->gray_thre_min, preprocess_param->gray_thre_max);
    //如果gray_r图像中的阈值大于thre，则为255，小于thre，则为0，并将最后结果保存再bin中
    cv::threshold(gray_b, bin, thre, 255, 0);
}
/*求出输入图像的所有像素值的平均值*/
int AdaptiveThreshold::avgGrayThreshold(cv::Mat src)
{
    long sum_gray = 0;
    //循环遍历输入图像的所有像素值
    for(int j = 0; j < src.rows; j++)
    {
        uchar *data = src.ptr<uchar>(j);
        for(int i = 0; i < src.cols; i++)
        {
            //求出图像中所有像素值的和
            sum_gray += data[i];
        }
    }
    //返回图像像素的平均值
    return sum_gray * 1.0 / (src.cols * src.rows);
}
/*对输入图像进行先膨胀后中值滤波的操作*/
void AdaptiveThreshold::filter(cv::Mat &dst)
{
    //cv::erode(dst, dst, element1);
    cv::dilate(dst, dst, element2);  //对输入图像进行膨胀处理（核为3x7的矩形核），并保存到原图像
//    setStructElement();
//    cv::morphologyEx(dst, dst, cv::MORPH_OPEN, element);
//    cv::morphologyEx(dst, dst,cv::MORPH_CLOSE,element);
    cv::medianBlur(dst, dst, 3);    //对图像进行中值滤波（核为3x3的矩形核），并保存到原图像
}
